// Copyright (c) 2008 Matthew Johnson
// Licensed under the MIT License (see license.txt)
package actionml.core {
	import actionml.Global;
	
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.FileReference;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;	
	import flash.utils.getTimer;

	public class LoadingUtils {
		// The URL can be absolute, relative, or just an extension.
		// If an extension is provided, a file with the same path
		// and name as the swf, but with the specified extension will
		// be loaded.
		//
		// The second argument can optionally be an object containing
		// data to send in a POST.
		//
		// TODO: update comments to account for changes - post data and callbacks are now in options
		//
		// After the URL, or after the post data (if post data is provided)
		// a callback function that accepts a single parameter is required.
		// It will be called with the data loaded from the file.
		//
		// An errorHandler callback may also be provided as the last argument
		// if desired.  This callback should accept a single argument and will
		// be provided with error information.
		//
		public static function initialize() {
		}
		public static function requestURL(URL:String, options:Object = null) {
			var request:URLRequest = new URLRequest(resolveURL(URL));
			appendData(request, options);

			var loader:URLLoader = new URLLoader();
			configureRequestHandlers(loader, options, "data");
			invokeWithErrorHandler(options.errorHandler, function() { loader.load(request); } );
		}

		public static function loadSwfOrImage(URL:String, options:Object = null) {
			var request:URLRequest = new URLRequest(resolveURL(URL));
			appendData(request, options);

			var loader:Loader = new Loader();
			configureRequestHandlers(loader.contentLoaderInfo, options, "content");			
			invokeWithErrorHandler(options.errorHandler, function() { loader.load(request, new LoaderContext(false, new ApplicationDomain(null))); } );
		}

		public static function uploadFile(URL:String, fieldName:String, options:Object = null) {
			var request:URLRequest = new URLRequest(resolveURL(URL));
			appendData(request, options);

			var fileRef:FileReference = new FileReference();
			configureRequestHandlers(fileRef, options);

			fileRef.addEventListener(Event.SELECT, function(e:Event) {
				if (options.fileSelectedHandler) {
					options.fileSelectedHandler(fileRef);
				}
				invokeWithErrorHandler(options.errorHandler, function() { e.target.upload(request, fieldName); } );
			});

			if (options.fileFilter is Array) { 
				fileRef.browse(options.fileFilter);
			} else if (options.fileFilter) {
				fileRef.browse([options.fileFilter]);
			} else {
				fileRef.browse();
			}
		}
		private static function invokeWithErrorHandler(errorHandler:Function, f:Function) {
			try {
				f();
			} catch (e:Error) {
				if (errorHandler is Function) {
					errorHandler(e);
				}
			}
		}
		private static function configureRequestHandlers(o:EventDispatcher, handlers:Object, completeHandlerArgumentName:String = null) {
			if (handlers.completeHandler is Function) {
				if (completeHandlerArgumentName == null) {
					o.addEventListener(Event.COMPLETE, handlers.completeHandler);
				} else { 
					o.addEventListener(Event.COMPLETE, function() {
						handlers.completeHandler(o[completeHandlerArgumentName]);
					});
				}
			} 
			if (handlers.statusHandler) {
				o.addEventListener(HTTPStatusEvent.HTTP_STATUS, handlers.statusHandler);
			}
			if (handlers.progressHandler) {
				o.addEventListener(ProgressEvent.PROGRESS, handlers.progressHandler);
			}
			if (handlers.errorHandler) {
				o.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handlers.errorHandler);
				o.addEventListener(IOErrorEvent.IO_ERROR, handlers.errorHandler);
			}
		}
		private static function appendData(request:URLRequest, options:Object) {
			if (options && options.data) {
				var variables:URLVariables = new URLVariables();
				for each (var field in options.data.getPropertyNames()) {
					variables[field] = options.data[field]
				}
				request.data = variables;
				request.method = URLRequestMethod.POST;
			}
		}
		private static function resolveURL(URL:String):String {
			if (URL.indexOf("://") == -1) { 
				// we have a relative URL or a file extension
				if (URL.charAt(0) == ".") {
					return replaceExtension(Global.stage.root.loaderInfo.url, URL.slice(1, URL.length));
				} else {
					return resolveRelativePath(URL);
				}
			} else {
				return URL;
			}
			function resolveRelativePath(relativePath:String) {
				return appendPath(moveUp(Global.stage.root.loaderInfo.url), relativePath);
			}
			function moveUp(s:String, distance:int = 1) {
				var r:String = s;
				for (var i = 0; i < distance; i++) {	
					r = r.replace(/\/[^\/]*$/, "");
				}
				return r;
			}
			function appendPath(s:String, p:String) {
				var r:String = s;
				while(/^\.\.\/ /x.test(p)) {
					r = moveUp(r);
					p = p.replace("../", "");
				}
				if (p.length > 0) {
					return r + "/" + p;
				} else {
					return r;
				}
			}
			function removeExtension(s:String) {
				return s.replace(/\.[^.]*$/, "");
			}
			function replaceExtension(s:String, newExtension:String) {
				return removeExtension(s) + "." + newExtension;
			}
		}
		
	}
	
}